<?php
/**********************************************************************************
WIKINDX: Bibliographic Management system.
Copyright (C)

This program is free software; you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program;
if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

The WIKINDX Team 2006
sirfragalot@users.sourceforge.net
**********************************************************************************/
/*****
*	Logging on and system authorisation class.
*****/
class AUTHORIZE
{
// Constructor
	function AUTHORIZE($db)
	{
		$this->db = $db;
		include_once("core/session/SESSION.php");
		$this->session = new SESSION();
		if(!$language = $this->session->getVar("setup_language"))
		{
// set the default language prior to displaying the login prompt
			$sql = $db->selectNoExecute(array('WKX_config'), array('language'));
			$recordset = $this->db->queryNoError($sql);
			if(!$recordset) // perhaps this is a first install
				$language = 'en';
			else
			{
				$language = $db->fetchOne($recordset);
				$this->session->setVar("setup_language", $language);
			}
		}
		include_once("core/messages/ERRORS.php");
		$this->errors = new ERRORS();
		include_once("core/messages/MESSAGES.php");
		$this->messages = new MESSAGES();
		include_once("core/user/USER.php");
		$this->user = new USER($this->db);
// Start the templating system
		include_once("core/template/TEMPLATE.php");
		$this->template = new TEMPLATE('content');
	}
// gatekeeper to the system.  Order is important!
	function gatekeep($vars)
	{
		include("config.php");
// logging out
		if(isset($vars["action"]) && ($vars["action"] == 'logout'))
			$this->logout();
// Forgotten password system
		if(isset($vars["action"]) && 
			(($vars["action"] == 'forgetInitStage1') ||
			($vars["action"] == 'forgetInitStage2') || 
			($vars["action"] == 'forgetProcess')))
			return TRUE;
		else if($vars && array_key_exists('action', $vars) && ($vars['action'] == 'upgradeDBLogon'))
			return $this->logonCheckUpgradeDB($vars['username'], $vars['password']);
// User supplying username and password to logon to WIKINDX.
// $auth->logonCheck() dies after printing logon screen if bad comparison.
		else if(isset($vars["action"]) && ($vars["action"] == 'logon') &&
			isset($vars["password"]) && isset($vars["username"]))
		{
			$this->logonCheck($vars['username'], $vars['password']);
// clear previous mywikindx session and, if this user has bibliographies, initialise mywikindx_bibliographies
// FALSE means go to front of WIKINDX
			return FALSE;
		}
// superAdmin already logged in after upgrade so just set up the environment
		else if(isset($vars["action"]) && ($vars["action"] == 'upgradeDB'))
		{
			$this->session->clearAllSessionData();
			$this->session->setVar("setup_userId", '1'); // superAdmin always id = '1'
			$this->session->setVar("setup_write", TRUE);
			$this->session->delVar("setup_readOnly");
			$this->user->writeSessionPreferences('1');
			$this->user->grabBibliographies();
// restore some session variables if stored from last logout
			$this->restoreEnvironment();
			return FALSE;
		}
// User requesting readOnly access - clear previous sessions
		else if(isset($vars["action"]) && ($vars["action"] == 'readOnly'))
		{
			if($WIKINDX_DENY_READONLY)
			{
				$pString = $this->initLogon(); // login prompt
				include_once("core/html/CLOSENOMENU.php");
				new CLOSENOMENU($this->db, $pString);
			}
// First delete any pre-existing session in case this user has been logging on and off as different users
// session array 'setup' is deleted at logout()
			$this->session->clearAllSessionData();
			$this->session->setVar("setup_readOnly", TRUE);
// populate session with default values from WKX_config
			$this->user->writeSessionPreferences(FALSE, 'WKX_config');
// FALSE means go to front of WIKINDX
			return FALSE;
		}
// User registration
		else if($this->session->getVar("setup_userRegistration") && $this->session->getVar("setup_multiUser"))
		{
			if($vars["action"] == 'initRegisterUser')
			{
				include_once("core/admin/ADMINUSER.php");
				$obj = new ADMINUSER($this->db, $vars);
				$pString =$obj->initRegister();
				if(!$this->session->getVar("setup_readOnly"))
				{
					include_once("core/html/CLOSENOMENU.php");
					new CLOSENOMENU($this->db, $pString);
				}
				else
				{
					include_once("core/html/CLOSE.php");
					new CLOSE($this->db, $pString);
				}
			}
			else if($vars["action"] == 'registerUser')
			{
				include_once("core/admin/ADMINUSER.php");
				$obj = new ADMINUSER($this->db, $vars);
				$pString =$obj->register();
				if(!$this->session->getVar("setup_readOnly"))
				{
					include_once("core/html/CLOSENOMENU.php");
					new CLOSENOMENU($this->db, $pString);
				}
				else
				{
					include_once("core/html/CLOSE.php");
					new CLOSE($this->db, $pString);
				}
			}
			else if($vars["action"] == 'registerConfirm')
			{
				include_once("core/admin/ADMINUSER.php");
				$obj = new ADMINUSER($this->db, $vars);
				$pString =$obj->registerConfirm();
				if(!$this->session->getVar("setup_readOnly"))
				{
					include_once("core/html/CLOSENOMENU.php");
					new CLOSENOMENU($this->db, $pString);
				}
				else
				{
					include_once("core/html/CLOSE.php");
					new CLOSE($this->db, $pString);
				}
			}
			else if($vars["action"] == 'registerUserAdd')
			{
				include_once("core/admin/ADMINUSER.php");
				$obj = new ADMINUSER($this->db, $vars);
				$pString =$obj->registerUserAdd();
				include_once("core/html/CLOSE.php");
				new CLOSE($this->db, $pString);
			}
			else if($vars["action"] == 'registerRequest')
			{
				include_once("core/admin/ADMINUSER.php");
				$obj = new ADMINUSER($this->db, $vars);
				$pString =$obj->registerRequest();
				include_once("core/html/CLOSE.php");
				new CLOSE($this->db, $pString);
			}
		}
		if(isset($vars["method"]) && ($vars['method'] == 'RSS') && !$WIKINDX_DENY_READONLY)
		{
			$this->session->setVar("setup_readOnly", TRUE);
			return TRUE;
		}
// access already granted
		if($this->session->getVar('setup_write') || $this->session->getVar("setup_readOnly"))
			return TRUE;
/*
// Try using HTTPS authentication
		if(isset($_SERVER['REMOTE_USER']))
		{
			$condition = " WHERE " . $this->db->formatField('username') . " = " . 
				$this->db->tidyInput($_SERVER['REMOTE_USER']);
			$recordset = $this->db->select(array('WKX_users'), array("id", "admin", "cookie"), $condition);
			if($this->db->numRows($recordset))
			{
// Logged in, now set up environment
				$row = $this->db->fetchRow($recordset);
// Only the superadmin may log on when multi user is not enabled
				if(!$this->session->getVar("setup_multiUser") && ($row['id'] != 1))
					return FALSE;
				$this->user->environment($row, $_SERVER['REMOTE_USER']); // Set up environment
				return TRUE;
			} // Else continue...
		}
*/
		include_once("core/cookie/COOKIE.php");
		$cookie = new COOKIE();
// grabCookie() returns TRUE if valid cookie - otherwise, proceed to manual logon
		if($cookie->grabCookie($this->db))
		{
// Success - so restore some session variables if stored from last logout
			$this->restoreEnvironment();
			return TRUE;
		}
		if(!isset($WIKINDX_DB_HOST))
			die($this->errors->text("dbError", "config"));
// Default == read only access.
		if(!$this->session->getVar("setup_write") && !$this->session->getVar('setup_readOnly'))
		{
			if($WIKINDX_READONLYACCESS && !$WIKINDX_DENY_READONLY)
			{
				$this->session->setVar("setup_readOnly", TRUE);
				return TRUE;
			}
			$pString = $this->initLogon(); // login prompt
			include_once("core/html/CLOSENOMENU.php");
			new CLOSENOMENU($this->db, $pString);
		}
// FALSE indicates that index.php will print the front page of WIKINDX
		return FALSE;
	}
// Display the empty form for logging on
	function initLogon($error = FALSE)
	{
		$this->session->delVar("setup_readOnly");
		include_once("core/html/FORM.php");
		include_once("core/html/MISC.php");
		include("config.php");
		global $WIKINDX_RESTRICT_USER;
		$pString = $error;
		$this->template->setVar('heading', $this->messages->text("heading", "logon"));
		if(!$this->session->getVar("setup_multiUser"))
			$pString .= MISC::p($this->errors->text("warning", "superadminOnly"));
		$this->template->setVar('formStart', FORM::formHeader("logon"));
		$pString .= MISC::p($this->messages->text("authorize", "writeLogon"));
		$link1 = "index.php?action=readOnly";
		$link2 = "index.php?action=forgetInitStage1";
		$link3 = "index.php?action=initRegisterUser";
		$links = FALSE;
/**
* For a test user (see index.php)
*/
		if($WIKINDX_RESTRICT_USER)
			$pString .= MISC::p("For test drive purposes, " .
				MISC::b($this->messages->text("user", "username") . ":&nbsp;&nbsp;") . "wikindx, " . 
				MISC::b($this->messages->text("user", "password") . ":&nbsp;&nbsp;") . "wikindx");
		$forgot = MISC::a("link", $this->messages->text("user", "forget6"), $link2);
		$pString .= $this->printLogonTable();
// Give user the option to bypass logging in simply to read.
		if(!$WIKINDX_DENY_READONLY)
			$links = MISC::a("link", $this->messages->text("authorize", "readOnly") . 
			MISC::br() . $forgot, $link1);
		else
			$links = $forgot;
		if($this->session->getVar("setup_userRegistration") && $this->session->getVar("setup_multiUser"))
			$links .= MISC::br() . MISC::a("link", $this->messages->text("menu", "register"), $link3);
		$pString .= MISC::p($links, FALSE, 'right');
		$this->template->setVar('body', $pString);
		return $this->template->process();
	}
// print username/password text boxes
	function printLogonTable()
	{
		include_once("core/html/FORM.php");
		include_once("core/html/TABLE.php");
		$pString = TABLE::tableStart(FALSE, 0, 0, 0, "left", "50%");
		$pString .= TABLE::trStart();
		$pString .= TABLE::td($this->messages->text("user", "username") . ":&nbsp;&nbsp;");
		$pString .= TABLE::td(FORM::textInput(FALSE, "username"));
		$pString .= TABLE::trEnd();
		$pString .= TABLE::trStart(FALSE);
		$pString .= TABLE::td($this->messages->text("user", "password") . ":&nbsp;&nbsp;");
		$pString .= TABLE::td(FORM::passwordInput(FALSE, "password"));
		$pString .= TABLE::trEnd();
		$pString .= TABLE::trStart();
		$pString .= TABLE::td("&nbsp;");
		$pString .= TABLE::td(FORM::formSubmit(), FALSE, "right");
		$pString .= TABLE::trEnd();
		$pString .= TABLE::tableEnd();
		$this->template->setVar('formEnd', FORM::formEnd());
		return $pString;
	}
// Initial logon to the system.
	function logonCheckUpgradeDB($username, $password)
	{
		$condition = " WHERE " . $this->db->formatField('username') . " = " . $this->db->tidyInput($username);
		$recordset = $this->db->select(array('WKX_users'), array("id", "password", "admin", "cookie"), 			
			$condition);
		if(!$this->db->numRows($recordset))
			return TRUE; // NB -- unable to authenticate
		$row = $this->db->fetchRow($recordset);
		if(crypt($password, $row['password']) != $row['password'])
			return TRUE;
		if($row['id'] != '1') // superAdmin is id '1'
			return TRUE;
		$this->session->setVar("setup_superadmin", TRUE);
		return FALSE;
	}
// Initial logon to the system.
	function logonCheck($username, $password)
	{
// If checkPassword is successful, it also sets up some session variables to allow access without reauthentication.
		if(!$this->user->checkPassword($username, $password))
			$this->failure();
// Success - so restore some session variables if stored from last logout
		$this->restoreEnvironment();
	}
// Successful registered user logon so restore some session variables
	function restoreEnvironment()
	{
		$recordset = $this->db->select(array('WKX_users'), 'userSession', 
			' WHERE ' . $this->db->formatField('id') . '=' . 
			$this->db->tidyInput($this->session->getVar('setup_userId')));
		$sessionData = $this->db->fetchOne($recordset);
		if($sessionData)
		{
			$array = explode('|', $sessionData);
			while($array)
			{
				$key = array_shift($array);
				$value = unserialize(base64_decode(array_shift($array)));
				$this->session->writeArray($value, $key);
			}
		}
	}
// log out user
	function logout()
	{
// Garbage disposal
// remove this session's files
		include("config.php");
		if(!isset($WIKINDX_FILES_DIR))
			$dir = "files";
		else
			$dir = $WIKINDX_FILES_DIR;
		if($sessVar = $this->session->getVar('fileExports'))
		{
			$sessArray = unserialize($sessVar);
			if($d = @opendir($dir))
			{
				while(FALSE !== ($f = readdir($d)))
				{
					if(($f == ".") || ($f == ".."))
						continue;
					if(array_search($f, $sessArray) === FALSE)
						continue;
					$file = $dir . "/" . $f;
					unlink($file);
				}
			}
			$this->session->delVar('fileExports');
		}
		if($sessVar = $this->session->getVar('paperExports'))
		{
			$sessArray = unserialize($sessVar);
			if($d = @opendir($dir))
			{
				while(FALSE !== ($f = readdir($d)))
				{
					if(($f == ".") || ($f == ".."))
						continue;
					if(!array_key_exists($f, $sessArray))
						continue;
					$file = $dir . "/" . $f;
					unlink($file);
				}
			}
			$this->session->delVar('paperExports');
		}
// As of v3.5, write the environment to the WKX_database
		$this->writeEnvironment();
		session_destroy();
// remove any wikindx cookie that has been set
		include_once("core/cookie/COOKIE.php");
		$cookie = new COOKIE();
		$cookie->deleteCookie();
		include_once("core/html/CLOSENOMENU.php");
		new CLOSENOMENU($this->db, $this->initLogon());
	}
// When logging out, write some of the session data to the database to be retrieved at logon
	function writeEnvironment($successMessage = TRUE)
	{
		$sessionKeys = array('exportRtf', 'exportHtml', 'export', 'sql', 'bookmark', 
			'exportPaper', 'list', 'select', 'search', 'powerSearch', 'searchMeta', 
			'selectMeta', 'basket');
		foreach($sessionKeys as $key)
		{
			if($value = $this->session->getArray($key))
			{
				$value = base64_encode(serialize($value));
				$sessionData[] = $key;
				$sessionData[] = $value;
			}
		}
// Join into |-delimited string
		$session['userSession'] = join('|', $sessionData);
		$this->db->update('WKX_users', $session, 
			" WHERE " . $this->db->formatField('id') . " = " . 
			$this->db->tidyInput($this->session->getVar('setup_userId')));
		if($successMessage)
		{
			include_once("core/messages/SUCCESS.php");
			$success = new SUCCESS();
			$this->template->setVar('body', $success->text("saveWikindxState"));
			return $this->template->process();
		}
	}
// Recovery of forgotten password stage1
	function forgetInitStage1($error = FALSE)
	{
		$this->template->setVar('heading', $this->messages->text("heading", "forget"));
		$pString = FORM::formHeader("forgetInitStage2");
		if($error)
			$pString .= $error;
		$pString .= MISC::p($this->messages->text("user", "forget7"));
		$string = FORM::textInput($this->messages->text("user", "username"), 
			"username", FALSE, 50, 255);
		$string .= MISC::br() . FORM::textInput($this->messages->text("user", "email"), 
			"email", FALSE, 50, 255);
		$pString .= MISC::p($string);
		$pString .= MISC::p(FORM::formSubmit("Proceed"));
		$pString .= FORM::formEnd();
		$this->template->setVar('body', $pString);
		return $this->template->process();
	}
// Recovery of forgotten password stage2.
// Input may be either username or email -- if both, use username
	function forgetInitStage2($vars, $error = FALSE)
	{
		if($username = trim($vars['username']))
		{
			$condition = " WHERE " . $this->db->formatField('username') . " = " . 
				$this->db->tidyInput($username);
		}
		else if($email = trim($vars['email']))
		{
			$condition = " WHERE " . $this->db->formatField('email') . " = " . 
				$this->db->tidyInput($email);
		}
		else
			$this->failureForget1($this->errors->text("inputError", "missing"));
		include_once("core/html/HTML.php");
		$html = new HTML();
		$this->template->setVar('heading', $this->messages->text("heading", "forget"));
		$pString = FORM::formHeader("forgetProcess");
		if($error)
			$pString .= $error;
		$pString .= MISC::p($this->messages->text("user", "forget8"));
		for($i = 1; $i < 4; $i++)
		{
			$userArray[] = "passwordQuestion$i";
			$userArray[] = "passwordAnswer$i";
		}
		$userArray[] = "username";
		$userArray[] = "email";
		$recordSet = $this->db->select(array('WKX_users'), $userArray, $condition);
		if($this->db->numRows($recordSet) > 1)
			$this->failureForget1($this->errors->text("warning", "forget2"));
		$row = $this->db->fetchRow($recordSet);
		$pString .= FORM::hidden("username", $row['username']);
		$pString .= FORM::hidden("email", $row['email']);
		$questionFound = FALSE;
		for($i = 1; $i < 4; $i++)
		{
			if(!$row["passwordQuestion$i"])
				continue;
			$string = $this->messages->text("user", "forget3", "&nbsp;" . $i) . 
				":&nbsp;&nbsp;" . $html->dbToHtmlTidy($row["passwordQuestion$i"]);
			$string .= MISC::br() . FORM::textInput(FALSE, "answer$i", FALSE, 50, 100);
			$pString .= MISC::p($string);
			$questionFound = TRUE;
		}
		if(!$questionFound)
		{
			$recordset = $this->db->select(array('WKX_config'), array("contactEmail"));
			$email = $this->db->fetchOne($recordset);
			if($email)
			{
				include_once("core/html/HTML.php");
				$html = new HTML();
				$email = $html->dbToHtmlTidy($email);
				$email = MISC::a("link", $email, "mailto:$email");
				$contact = "&nbsp;($email).";
			}
			else
				$contact = ".";
			$this->failure($this->errors->text("warning", "forget1", $contact));
		}
		$pString .= MISC::p(FORM::formSubmit("Proceed"));
		$pString .= FORM::formEnd();
		$this->template->setVar('body', $pString);
		return $this->template->process();
	}
// Process forgotten password request
	function forgetProcess($vars)
	{
		$username = trim($vars['username']);
		$condition = " WHERE " . $this->db->formatField('username') . " = " . 
			$this->db->tidyInput($username);
		for($i = 1; $i < 4; $i++)
		{
			$userArray[] = "passwordQuestion$i";
			$userArray[] = "passwordAnswer$i";
		}
		$row = $this->db->fetchRow($this->db->select(array('WKX_users'), $userArray, $condition));
		for($i = 1; $i < 4; $i++)
		{
			if(!array_key_exists("answer$i", $vars))
				continue;
			$answer = sha1(strtolower(trim($vars["answer$i"])));
			if($answer != $row["passwordAnswer$i"])
				$this->failureForget2($vars, $this->errors->text("inputError", "incorrect"));
		}
		$password = time();
		include_once("core/email/EMAIL.php");
		$emailClass = new EMAIL(FALSE, $vars);
		if(!$emailClass->forgetProcess($username, $password))
		{
			$recordset = $this->db->select(array('WKX_config'), array("contactEmail"));
			$email = $this->db->fetchOne($recordset);
			if($email)
			{
				include_once("core/html/HTML.php");
				$html = new HTML();
				$email = $html->dbToHtmlTidy($email);
				$email = MISC::a("link", $email, "mailto:$email");
				$contact = "&nbsp;($email).";
			}
			else
				$contact = ".";
			$this->failure($this->errors->text("warning", "forget3", $contact));
		}
// If we get here, all questions have been correctly answered so write temp pass word to database and send out email.
// NB  This is done after sending out email.  If email fails, we don't want to change the user's password.  However, the risk is that a password may be sent 
// and then the update code below will fail.  This is judged to be the lesser of two evils.
		$this->template->setVar('heading', $this->messages->text("heading", "forget"));
		$cryptPassword = crypt($password, strrev(time()));
		$this->db->updateSingle('WKX_users', 
			$this->db->formatField('password') .  "=" . $this->db->tidyInput($cryptPassword), 
			" WHERE " . $this->db->formatField('username') . '=' . $this->db->tidyInput($username));
		$pString = $this->messages->text("user", "forget10");
		$link = "index.php?action=initLogon";
		$pString .= MISC::p(MISC::a("link", $this->messages->text("user", "forget11"), $link), FALSE, "right");
		$this->template->setVar('body', $pString);
		return $this->template->process();
	}
// failure
	function failure($error = FALSE)
	{
// Exit back to logon prompt
		include_once("core/html/CLOSENOMENU.php");
		new CLOSENOMENU($this->db, $this->initLogon($error));
	}
// failure for forgotten password stage 1
	function failureForget1($error)
	{
		include_once("core/html/CLOSENOMENU.php");
		new CLOSENOMENU($this->db, $this->forgetInitStage1($error));
	}
// failure for forgotten password stage 2
	function failureForget2($vars, $error)
	{
		include_once("core/html/CLOSENOMENU.php");
		new CLOSENOMENU($this->db, $this->forgetInitStage2($vars, $error));
	}
}
?>
